//-----------------------------------------------------------------------------
// File: skybox.cpp
//
// Desc: Encapsulation of skybox geometry and textures
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#include "DXUT.h"
#include "SDKmisc.h"
#include "skybox.h"
extern void travedir(const char* pdir, const char* suffix, char re[][MAX_PATH], int* num);

struct SKYBOX_VERTEX
{
    D3DXVECTOR4 pos;
    D3DXVECTOR3 tex;
};


D3DVERTEXELEMENT9 g_aSkyboxDecl[] =
{
    { 0, 0,  D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
    D3DDECL_END()
};

LPWSTR faceName[6] = {L"\\posx.jpg\0", L"\\negx.jpg\0", L"\\posy.jpg\0", L"\\negy.jpg\0", L"\\posz.jpg\0", L"\\negz.jpg\0"};




//-----------------------------------------------------------------------------
// Name: CSkybox
// Desc: Constructor
//-----------------------------------------------------------------------------
CSkybox::CSkybox()
{
    m_pCubeVB = NULL;
    m_pd3dDevice = NULL;
    m_pEffect = NULL;
    m_pVertexDecl = NULL;
    g_pCubeMapTexture = NULL;
	g_nCubeMapData=0;
	g_nCubeMapChosen=0;
	g_CubeMapWidth = 1024;
	g_CubeMapHeight= 1024;
	g_strEffectFileName = L"Skybox.fx";

	
}

HRESULT CSkybox::OpenCubeFace(D3DCUBEMAP_FACES FaceType, LPDIRECT3DCUBETEXTURE9 CubeTex, char* dir)
{
	HRESULT hr;
	WCHAR fileName[MAX_PATH];
	LPDIRECT3DSURFACE9 psurfOrig = NULL;
	hr = CubeTex->GetCubeMapSurface(FaceType, 0, &psurfOrig);

	MultiByteToWideChar(CP_ACP,0, dir, -1, fileName, MAX_PATH);
	//wcscpy_s(fileName, g_CubeMapFilePath[g_nCubeMapChosen]);
	wcscat_s(fileName, MAX_PATH, faceName[(int)FaceType]);

	if (FAILED(D3DXLoadSurfaceFromFile(psurfOrig, NULL, NULL, fileName, NULL, D3DX_DEFAULT, 0, NULL)))
	{
		return FALSE;
	}
	SAFE_RELEASE(psurfOrig);
	return TRUE;
}

HRESULT CSkybox::OnLoadCubeMapTexutre()
{
	// Obtain maximum texture dimension
	D3DCAPS9 d3dcaps;
	HRESULT hr;
	if ( SUCCEEDED( m_pd3dDevice->GetDeviceCaps( &d3dcaps ) ) )
	{
		if (d3dcaps.MaxTextureWidth<g_CubeMapWidth||d3dcaps.MaxTextureHeight<g_CubeMapHeight)
		{
			printf("Cubemap size overflow!\n");
			return FALSE;
		}
	}
	if (g_pCubeMapTexture!=NULL)
	{
		SAFE_RELEASE(g_pCubeMapTexture);
	}

	V_RETURN(m_pd3dDevice->CreateCubeTexture(g_CubeMapWidth, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &g_pCubeMapTexture, NULL));

	for (int i=0; i<6; i++)
	{
		V_RETURN(OpenCubeFace((D3DCUBEMAP_FACES)i, g_pCubeMapTexture, g_CubeMapFilePath[g_nCubeMapChosen]));
	}
	return TRUE;
}

HRESULT CSkybox::OnCreateDevice(IDirect3DDevice9* pd3dDevice, WCHAR* strEffectFileName, int useCubeMap)
{
	HRESULT hr;
	m_pd3dDevice = pd3dDevice;

	if (useCubeMap)
		V_RETURN(OnLoadCubeMapTexutre());

	V_RETURN(OnCreateDevice( strEffectFileName ));

	return TRUE;
}



//-----------------------------------------------------------------------------
HRESULT CSkybox::OnCreateDevice( LPDIRECT3DDEVICE9 pd3dDevice, WCHAR* strCubeMapFile, WCHAR* strEffectFileName )
{
    HRESULT hr;

	m_pd3dDevice = pd3dDevice;


    WCHAR strPath[MAX_PATH];
    V_RETURN( DXUTFindDXSDKMediaFileCch( strPath, MAX_PATH, strCubeMapFile ) );

    V_RETURN( D3DXCreateCubeTextureFromFileEx( m_pd3dDevice, strPath, D3DX_DEFAULT, 1, 0, D3DFMT_A16B16G16R16F,
                                               D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, NULL,
                                               NULL, &g_pCubeMapTexture ) );

    return OnCreateDevice( strEffectFileName );
}


//-----------------------------------------------------------------------------
HRESULT CSkybox::OnCreateDevice(WCHAR* strEffectFileName )
{
    HRESULT hr;
    

    // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the shader debugger.  
    // Debugging vertex shaders requires either REF or software vertex processing, and debugging 
    // pixel shaders requires REF.  The D3DXSHADER_FORCE_*_SOFTWARE_NOOPT flag improves the debug 
    // experience in the shader debugger.  It enables source level debugging, prevents instruction 
    // reordering, prevents dead code elimination, and forces the compiler to compile against the next 
    // higher available software target, which ensures that the unoptimized shaders do not exceed 
    // the shader model limitations.  Setting these flags will cause slower rendering since the shaders 
    // will be unoptimized and forced into software.  See the DirectX documentation for more information 
    // about using the shader debugger.
    DWORD dwShaderFlags = D3DXFX_NOT_CLONEABLE;

    // Read the D3DX effect file
    WCHAR str[MAX_PATH];
    V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, strEffectFileName ) );

    // If this fails, there should be debug output as to 
    // they the .fx file failed to compile
    V_RETURN( D3DXCreateEffectFromFile( m_pd3dDevice, str, NULL, NULL,
                                        dwShaderFlags, NULL, &m_pEffect, NULL ) );

    // Create vertex declaration
    V_RETURN( m_pd3dDevice->CreateVertexDeclaration( g_aSkyboxDecl, &m_pVertexDecl ) );

    return S_OK;
}

void CSkybox::SetCubeMapData()
{
	if(g_nCubeMapData)	return;

	WCHAR wFileName[MAX_PATH];
	GetModuleFileName(NULL, wFileName, MAX_PATH);
	WCHAR* dir = wcsrchr(wFileName, L'\\');
	*dir = L'\0';
	dir = wcsrchr(wFileName, L'\\');
	*dir = L'\0';
	wcscat_s(wFileName, MAX_PATH, L"\\..\\..\\model\\CubeMaps\\_Thumbnail\0");
	char tmp[MAX_PATH];
	WideCharToMultiByte(CP_ACP,0, wFileName, -1, tmp+0, MAX_PATH, NULL,NULL);
	travedir(tmp, ".jpg\0", g_CubeMapFilePath, &g_nCubeMapData);

	char *pExt;
	for (int i=0; i<g_nCubeMapData; i++)
	{
		pExt = strrchr(g_CubeMapFilePath[i],'.');
		*pExt = '\0';
		pExt = strrchr(g_CubeMapFilePath[i],'\\');
		strcpy_s(tmp, pExt);
		*pExt = '\0';
		pExt = strrchr(g_CubeMapFilePath[i],'\\');
		*pExt = '\0';
		strcat_s(g_CubeMapFilePath[i], tmp);

		//if(pExt&&stricmp(pExt, suffix)==0)


#if defined(DEBUG)||defined(_DEBUG)
		printf("%d cubemap file: %s \n", i, g_CubeMapFilePath[i]);
#endif

		pExt = strrchr(g_CubeMapFilePath[i],'\\');
		MultiByteToWideChar(CP_ACP,0, pExt+1, -1, g_CubeMapName[i], MAX_NAME_LEN);
		//strcpy_s(g_CubeMapName[i], pExt+1);
	}
}

//-----------------------------------------------------------------------------
void CSkybox::OnResetDevice( const D3DSURFACE_DESC* pBackBufferSurfaceDesc )
{
    HRESULT hr;

    if( m_pd3dDevice == NULL )
        return;

    if( m_pEffect )
        V( m_pEffect->OnResetDevice() );

    V( m_pd3dDevice->CreateVertexBuffer( 4 * sizeof( SKYBOX_VERTEX ),
                                         D3DUSAGE_WRITEONLY, 0,
                                         D3DPOOL_DEFAULT, &m_pCubeVB, NULL ) );

    // Fill the vertex buffer
    SKYBOX_VERTEX* pVertex = NULL;
    V( m_pCubeVB->Lock( 0, 0, ( void** )&pVertex, 0 ) );

    // Map texels to pixels 
    float fHighW = -1.0f - ( 1.0f / ( float )pBackBufferSurfaceDesc->Width );
    float fHighH = -1.0f - ( 1.0f / ( float )pBackBufferSurfaceDesc->Height );
    float fLowW = 1.0f + ( 1.0f / ( float )pBackBufferSurfaceDesc->Width );
    float fLowH = 1.0f + ( 1.0f / ( float )pBackBufferSurfaceDesc->Height );

    pVertex[0].pos = D3DXVECTOR4( fLowW, fLowH, 1.0f, 1.0f );
    pVertex[1].pos = D3DXVECTOR4( fLowW, fHighH, 1.0f, 1.0f );
    pVertex[2].pos = D3DXVECTOR4( fHighW, fLowH, 1.0f, 1.0f );
    pVertex[3].pos = D3DXVECTOR4( fHighW, fHighH, 1.0f, 1.0f );

    m_pCubeVB->Unlock();
}


//-----------------------------------------------------------------------------
// Name: Render
// Desc: 
//-----------------------------------------------------------------------------
void CSkybox::Render()
{
    HRESULT hr;

	V( m_pEffect->SetMatrix( "g_SkyboxInvWVP", &g_invMVP) );

    // Draw the skybox
    UINT uiPass, uiNumPasses;
    V( m_pEffect->SetTechnique( "RenderCubeMap" ) );
    V( m_pEffect->SetTexture( "g_txCubeMap", g_pCubeMapTexture ) );

    m_pd3dDevice->SetStreamSource( 0, m_pCubeVB, 0, sizeof( SKYBOX_VERTEX ) );
    m_pd3dDevice->SetVertexDeclaration( m_pVertexDecl );

    V( m_pEffect->Begin( &uiNumPasses, 0 ) );
    for( uiPass = 0; uiPass < uiNumPasses; uiPass++ )
    {
        V( m_pEffect->BeginPass( uiPass ) );
        m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 );
        V( m_pEffect->EndPass() );
    }
    V( m_pEffect->End() );
}


//-----------------------------------------------------------------------------
void CSkybox::OnLostDevice()
{
    HRESULT hr;
    if( m_pEffect )
        V( m_pEffect->OnLostDevice() );
    SAFE_RELEASE( m_pCubeVB );
}


//-----------------------------------------------------------------------------
void CSkybox::OnDestroyDevice()
{
    m_pd3dDevice = NULL;
    SAFE_RELEASE( g_pCubeMapTexture );
    SAFE_RELEASE( m_pEffect );
    SAFE_RELEASE( m_pVertexDecl );
}




